//go:build !no_file_scan
// +build !no_file_scan

/*
Copyright 2022 The Authors of https://github.com/CDK-TEAM/CDK .

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package credential_access

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/cdk-team/CDK/pkg/exploit/base"

	"github.com/cdk-team/CDK/conf"
	"github.com/cdk-team/CDK/pkg/cli"
	"github.com/cdk-team/CDK/pkg/plugin"
)

func SearchLocalFileText(StartDir string) {
	_ = filepath.Walk(StartDir, func(path string, info os.FileInfo, err error) error {

		// broken soft-link file will trigger panic in os.Stat().xxx()
		defer func() {
			if err := recover(); err != nil {

			}
		}()

		fileInfo, _ := os.Stat(path)

		// skip soft-link
		//if util.IsSoftLink(path) {
		//	println("skip ln:", path)
		//	return nil
		//}
		// drop dir, only scan files
		if fileInfo.IsDir() {
			return nil
		}
		// skip large file
		if fileInfo.Size() > conf.ScanFileTextConf.MaxFileByte {
			return filepath.SkipDir
		}
		// skip executable file
		if conf.ScanFileTextConf.SkipExecutableFile && strings.Contains(fileInfo.Mode().String(), "x") {
			return filepath.SkipDir
		}

		// read file text
		data, err := ioutil.ReadFile(path)
		if err != nil {
			//log.Fatal(err) //ignore fatal errors
		}

		// apply regexp match
		for k, v := range conf.ScanFileTextConf.RegexList {
			pattern := regexp.MustCompile(v)
			params := pattern.FindAllStringSubmatch(string(data), -1)
			for _, matched := range params {
				fmt.Printf("\nfound %s in: %s\n%s\n", k, path, matched)
			}
		}

		return nil
	})
}

// plugin interface
type FileScanS struct{ base.BaseExploit }

func (p FileScanS) Desc() string {
	return "search AK/Secrets from input dir, usage: cdk run ak-leakage <dir>"
}
func (p FileScanS) Run() bool {
	if len(cli.Args["<args>"].([]string)) != 1 {
		log.Println("Invalid input args.")
		log.Fatal(p.Desc())
	}
	path := cli.Args["<args>"].([]string)[0]
	log.Println("searching secrets in ", path)
	SearchLocalFileText(path)
	log.Println("finished.")
	return true
}

func init() {
	exploit := FileScanS{}
	exploit.ExploitType = "credential-access"
	plugin.RegisterExploit("ak-leakage", exploit)
}
